﻿#light

namespace FSL.FMathLib

  module Permutation =
    
    /// Класс предоставляющий все слова длины n над алфавитом ABC
    /// n - длина слов
    /// ABC - алфавит
    type Permutator<'a>(n: int, abc: #seq<'a>) =
      let ABC = List.ofSeq abc  // нумерованный алфавит
      let N = List.length ABC   // мощность алфавита
      let counters = Array.create n 0   // список счётчиков
  
      /// Предикат оределяющий закончел ли перебор значений
      member private this.is_end() =
        Array.forall (fun counter -> counter = (N-1)) counters 

      /// Сброс значений счётчиков
      member this.Reset() =
        Array.iteri (fun i _ -> counters.[i] <- 0) counters
  
      /// Добавление единицы к счётчикам
      member private this.add_one() =
        let flag = ref true
        Array.iteri (fun i a -> 
          if !flag then
            if a = N-1 && i <> (N-1)
              then counters.[i] <- 0
              elif i <> (N-1) 
                then 
                  counters.[i] <- a+1
                  flag := false
                elif a <> N-1
                  then 
                    counters.[i] <- a+1
                    flag := false
          ) counters
      
      /// Получение лениво вычисляемой последовательности слов над алфавитом ABC
      member this.Words 
        with get() =
          seq { while this.is_end() = false do
                  yield Array.map (fun i -> ABC.[i]) counters
                  this.add_one()
                yield Array.map (fun i -> ABC.[i]) counters
            }

